package com.barnaby.sudoku;

import java.util.*;

public final class Board implements IBoard {
	public static IBoard MakeNewBoard(){
		return new Board();
	}
	
	private Board(){
		cells = new Cell[MaxColumn + 1][MaxRow + 1];
		ResetBoard();
	}

	private final byte MaxRow = 8;
	private final byte MinRow = 0;
	private final byte MaxColumn = 8;
	private final byte MinColumn = 0;
	
	private Cell cells[][];
	
	public boolean IsValid(){
		return true;
	}

	@Override
	public void ResetBoard() {
		for(int row = 0;row <= MaxRow;row++){
			for(int column = 0;column <= MaxColumn;column++){
				cells[column][row] = new Cell();
			}
		}
	}

	@Override
	public boolean TrySetCellValue(int column, int row, int value){
		if(!GetPossibleCellValues(column, row).contains(value))
			return false;
		return cells[column][row].setValue(value);
		
	}

	@Override
	public boolean TrySetBoardCellValues(String values) {
		for(int row = MinRow;row <= MaxRow;row++){
			for(int column = MinColumn;column <= MaxColumn;column++){
				int offset = row*(MaxRow + 1) + column;
				cells[column][row].setValue(Integer.parseInt(values.substring(offset, offset + 1)));
			}
		}
		return true;
	}

	@Override
	public int GetCellValue(int column, int row) {
		return this.cells[column][row].getValue();
	}

	@Override
	public String GetBoardCellValues() {
		String output = "";
		for(int row = MinRow;row <= MaxRow;row++){
			for(int column = MinColumn;column <= MaxColumn;column++){
				output += cells[column][row].getValue();
			}
		}
		return output;
	}

	@Override
	public LinkedHashSet<Integer> GetUniquePossibleCellValues(int column, int row) {
		
		LinkedHashSet<Integer> output = getUniquePossibleCellValuesForRow(column, row);
		output.addAll(getUniquePossibleCellValuesForColumn(column, row));
		output.addAll(getUniquePossibleCellValuesForBlock(column, row));
		
		return output;
	}
	
	private LinkedHashSet<Integer> getUniquePossibleCellValuesForBlock(int column, int row) {
		LinkedHashSet<Integer> output = this.GetPossibleCellValues(column, row); 
		LinkedHashSet<Integer> possible;
		
		if(output.contains(0))
			output.remove(0);
		
		int minCol, maxCol, minRow, maxRow;
	
		if(column < 3) {minCol = 0; maxCol = 2;}	
		else if(column > 5) {minCol = 6; maxCol = 8;}
			else {minCol = 3; maxCol =5;}
		
		if(row < 3) {minRow = 0; maxRow = 2;}
		else if(row > 5) {minRow = 6; maxRow = 8;}
			else {minRow = 3; maxRow =5;}
		
		for(int col = minCol; col <= maxCol; col++){
			for(int ro = minRow; ro <= maxRow; ro++){
				if(col != column && ro != row){
					possible = this.GetPossibleCellValues(col, row);
					for(int cursor : possible){
						if(output.contains(cursor))
							output.remove(cursor);
					}
				}
			}
		}
		return output;
	}

	private LinkedHashSet<Integer> getUniquePossibleCellValuesForRow(int column, int row) {
		LinkedHashSet<Integer> output = this.GetPossibleCellValues(column, row); 
		LinkedHashSet<Integer> possible;
		
		if(output.contains(0))
			output.remove(0);
		
		for(int col = MinColumn; col <= MaxColumn; col++){
			if(col == column)
				continue;
			
			if(this.cells[col][row].value != 0){
				if(output.contains(this.cells[col][row].value))
					output.remove(this.cells[col][row].value);
			}else{
				possible = this.GetPossibleCellValues(col, row);
				for(int cursor : possible){
					if(output.contains(cursor))
						output.remove(cursor);
				}
			}
		}
		return output;
	}
	
	private LinkedHashSet<Integer> getUniquePossibleCellValuesForColumn(int column, int row) {
		LinkedHashSet<Integer> output = this.GetPossibleCellValues(column, row); 
		LinkedHashSet<Integer> possible;
		
		if(output.contains(0))
			output.remove(0);
		
		for(int ro = MinRow; ro <= MaxRow; ro++){
			if(ro == row)
				continue;
			
			if(this.cells[column][ro].value != 0){
				if(output.contains(this.cells[column][ro].value))
					output.remove(this.cells[column][ro].value);
			}else{
				possible = this.GetPossibleCellValues(column, ro);
				for(int cursor : possible){
					if(output.contains(cursor))
						output.remove(cursor);
				}
			}
		}
		return output;
	}
	
	

	@Override
	public LinkedHashSet<Integer> GetPossibleCellValues(int column, int row) {
		LinkedHashSet<Integer> output = new LinkedHashSet<Integer>();
		for(int value = 1; value < 10; value++)
			output.add(value);
		
		for(int col = MinColumn; col <= MaxColumn; col++)
			if(col != column) 
				output.remove(GetCellValue(col, row));
		
		for(int ro = MinRow; ro <= MaxRow; ro++)
			if(ro != row) 
				output.remove(GetCellValue(column, ro));
		
		int minCol, maxCol, minRow, maxRow;
		
		if(column < 3) {minCol = 0; maxCol = 2;}
		else if(column > 5) {minCol = 6; maxCol = 8;}
		else {minCol = 3; maxCol =5;}
		
		if(row < 3) {minRow = 0; maxRow = 2;}
		else if(row > 5) {minRow = 6; maxRow = 8;}
		else {minRow = 3; maxRow =5;}
		
		for(int col = minCol; col <= maxCol; col++){
			for(int ro = minRow; ro <= maxRow; ro++){
				if(col != column && ro != row)
					output.remove(GetCellValue(col, ro));
			}
		}
							
		output.add(0);
		
		return output;
	}

	@Override
	public boolean IsComplete() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean TrySolve() {
		// TODO Auto-generated method stub
		return false;
	}
}
